# -*- coding: utf-8 -*-

'''
Created on 26/11/2011

@author: Luis M. Ugalde
www.forondarena.net

Copyright © 2011 Luis M. Ugalde
Licensed under the terms of the MIT License
(see LICENSE file for details)

This software can't be considered to be even in Alpha state. It still needs a lot of work,
starting with a proper error detection and exception handling.
'''


try:
    import sys
    import os
    import argparse
    
    import config
    
    from readers import readeradapter
    from statestores import statestoreadapter
    from parsers import parseradapter
    from writers import writeradapter
    
    from readers import readerexception
    from parsers import parserexception
    from statestores import statestoreexception
    from writers import writerexception
    
    from  multiprocessing import Process
    
    import time
except ImportError:
    print "At this moment, you need everything to run the application"
    sys.exit()


def readerProcess(usedclass, availableclasses, instance):
    print "Forking a reader of type {}.".format(usedclass)
    try:
        reader = readeradapter.Reader(readertype = usedclass, availablereaders = availableclasses)
    except readerexception.ReaderException:
        print "Error in reader"
        sys.exit()
        
    try:
        for i in range(instance.getDataSourceLen()):
            print "Please note that file readers will only use the last defined source."
            reader.setSource(srcfilename = instance.getDataSourceConfig(i, 'srcfilename'))
    except config.ConfigException:
        print "Instance data source configuration is invalid. Not starting this reader"
        sys.exit()
    except readerexception.ReaderException:
        print "Error in reader"
        sys.exit()
            
    reader.connectSources()
    
    try:
        reader.setBindReader(proto = instance.getDataBindConfig('proto'),
                             host = instance.getDataBindConfig('host'),
                             port = instance.getDataBindConfig('port'))
    except config.ConfigException:
        print "Instance data bind configuration is invalid. Not starting this reader"
        sys.exit()
    except readerexception.ReaderException:
        print "Error in reader"
        sys.exit()
        
    reader.bindReader()
    
    # Readers don't connect to notify sources
    
    try:
        reader.setBindNotifyReader(proto = instance.getNotifyBindConfig('proto'),
                                   host = instance.getNotifyBindConfig('host'),
                                   port = instance.getNotifyBindConfig('port'))
    except config.ConfigException:
        print "Instance notify bind configuration is invalid. Not starting this reader"
        sys.exit()

    reader.bindNotifyReader()

    reader.getLines()


def parserProcess(usedclass, availableclasses, instance):
    print "Forking a parser of type {}.".format(usedclass)
    try:
        parser = parseradapter.Parser(parsertype = usedclass, 
                                      availableparsers = availableclasses,
                                      helperfilename = instance.getOptionsConfig('helperfilename'))
    except parserexception.ParserException:
        print "Error in parser"
        sys.exit()
        
    try:
        for i in range(instance.getDataSourceLen()):
            parser.setSource(proto = instance.getDataSourceConfig(i, 'proto'),
                             host = instance.getDataSourceConfig(i, 'host'),
                             port = instance.getDataSourceConfig(i, 'port'))
    except config.ConfigException:
        print "Instance data source configuration is invalid. Not starting this parser"
        sys.exit()

    parser.connectSources()
    
    try:
        parser.setBindParser(proto = instance.getDataBindConfig('proto'),
                             host = instance.getDataBindConfig('host'),
                             port = instance.getDataBindConfig('port'))
    except config.ConfigException:
        print "Instance data bind configuration is invalid. Not starting this parser"
        sys.exit()

    parser.bindParser()
    
    try:
        for i in range(instance.getNotifySourceLen()):
            parser.setNotifySource(proto = instance.getNotifySourceConfig(i, 'proto'),
                                   host = instance.getNotifySourceConfig(i, 'host'),
                                   port = instance.getNotifySourceConfig(i, 'port'))
    except config.ConfigException:
        print "Instance notify source configuration is invalid. Not starting this parser"
        sys.exit()

    parser.connectNotifySources()
    
    try:
        parser.setBindNotifyParser(proto = instance.getNotifyBindConfig('proto'),
                                   host = instance.getNotifyBindConfig('host'),
                                   port = instance.getNotifyBindConfig('port'))
    except config.ConfigException:
        print "Instance notify bind configuration is invalid. Not starting this parser"
        sys.exit()
        
    parser.bindNotifyParser()

    parser.parseLines()
    

def stateStoreProcess(usedclass, availableclasses, instance):
    print "Forking a statestore of type {}.".format(usedclass)
    try:
        statestore = statestoreadapter.StateStore(statestoretype = usedclass,
                                                  availablestatestores = availableclasses, 
                                                  shelvefilename = instance.getOptionsConfig('shelvefilename'), 
                                                  shelvewriteback = instance.getOptionsConfig('shelvewriteback'))
    except statestoreexception.StateStoreException:
        print "Error in state store"
        sys.exit()

    try:
        for i in range(instance.getDataSourceLen()):
            statestore.setSource(proto = instance.getDataSourceConfig(i, 'proto'), 
                                 host = instance.getDataSourceConfig(i, 'host'), 
                                 port = instance.getDataSourceConfig(i, 'port'))
    except config.ConfigException:
        print "Instance data source configuration is invalid. Not starting this statestore"
        sys.exit()

    statestore.connectSources()
    
    try:
        statestore.setBindStateStore(proto = instance.getDataBindConfig('proto'),
                                     host = instance.getDataBindConfig('host'),
                                     port = instance.getDataBindConfig('port'))
    except config.ConfigException:
        print "Instance data bind configuration is invalid. Not starting this statestore"
        sys.exit()

    statestore.bindStateStore()

    try:
        for i in range(instance.getNotifySourceLen()):
            statestore.setNotifySource(proto = instance.getNotifySourceConfig(i, 'proto'),
                                       host = instance.getNotifySourceConfig(i, 'host'),
                                       port = instance.getNotifySourceConfig(i, 'port'))
    except config.ConfigException:
        print "Instance notify source configuration is invalid. Not starting this statestore"
        sys.exit()

    statestore.connectNotifySources()

    try:
        statestore.setBindNotifyStateStore(proto = instance.getNotifyBindConfig('proto'),
                                           host = instance.getNotifyBindConfig('host'),
                                           port = instance.getNotifyBindConfig('port'))
    except config.ConfigException:
        print "Instance notify bind configuration is invalid. Not starting this statestore"
        sys.exit()

    statestore.bindNotifyStateStore()

    statestore.processLines()
 

def writerProcess(usedclass, availableclasses, instance):
    print "Forking a writer of type {}.".format(usedclass)
    try:
        writer = writeradapter.Writer(writertype = usedclass, availablewriters = availableclasses)
    except writerexception.WriterException:
        print "Error in writer"
        sys.exit()

    try:
        for i in range(instance.getDataSourceLen()):
            writer.setSource(proto = instance.getDataSourceConfig(i, 'proto'),
                             host = instance.getDataSourceConfig(i, 'host'),
                             port = instance.getDataSourceConfig(i, 'port'))
    except config.ConfigException:
        print "Instance data source configuration is invalid. Not starting this writer"
        sys.exit()

    writer.connectSources()

    try:
        writer.setBindWriter(dstfilename = instance.getDataBindConfig('dstfilename'))
    except config.ConfigException:
        print "Instance data bind configuration is invalid. Not starting this writer"
        sys.exit()

    writer.bindWriter()

    try:
        for i in range(instance.getNotifySourceLen()):
            writer.setNotifySource(proto = instance.getNotifySourceConfig(i, 'proto'),
                                   host = instance.getNotifySourceConfig(i, 'host'),
                                   port = instance.getNotifySourceConfig(i, 'port'))
    except config.ConfigException:
        print "Instance notify source configuration is invalid. Not starting this statestore"
        sys.exit()

    writer.connectNotifySources()

    writer.putRecords()
 

if __name__ == '__main__':
    ### TODO, change pattern to check if listeners (pull) exist before sending data (push)
    ## Process forking order is not important here.
    
    configparser = argparse.ArgumentParser(description='Parse and store log files')
    configparser.add_argument('-c', '--config', nargs='?', const='config.yaml', default='config.yaml', help='Configuration file. Default is config.yaml')
    args = configparser.parse_args()

    if not os.path.isfile(args.config):
        print "Please use a valid configuration filename"
        sys.exit()

    configuration = config.Config(args.config)
    del(args)

    if configuration.startReader():
        tmplen = configuration.getReaderInstanceNumber()
        print "Found reader configuration. Need to fork {} instances".format(tmplen)

        i = 0
        readers = []
        while i < tmplen:
            try:
                tmpinstance = configuration.getReaderConfigInstance(i)
                readers.append(Process(target=readerProcess,
                                       args=(configuration.getReaderConfig('usedclass'), configuration.getReaderConfig('availableclasses'), tmpinstance)))
                readers[i].start()
            except config.ConfigException:
                print "Reader instance data is not valid. Skipping"
            finally:
                i += 1

    time.sleep(2)
    
    if configuration.startParser():
        tmplen= configuration.getParserInstanceNumber()
        print "Found parser configuration. Need to fork {} instances".format(tmplen)

        i = 0
        parsers = []
        while i < tmplen:
            try:
                tmpinstance = configuration.getParserConfigInstance(i)
                parsers.append(Process(target=parserProcess,
                                       args=(configuration.getParserConfig('usedclass'), configuration.getParserConfig('availableclasses'), tmpinstance)))
                parsers[i].start()
            except config.ConfigException:
                print "Instance data is not valid. Skipping"
            finally:
                i += 1
            
    time.sleep(2)

    if configuration.startStateStore():
        tmplen = configuration.getStateStoreInstanceNumber()
        print "Found State Store configuration. Need to fork {} instances".format(tmplen)

        i = 0
        statestores = []
        while i < tmplen:
            try:
                tmpinstance = configuration.getStateStoreConfigInstance(i)
                statestores.append(Process(target=stateStoreProcess,
                                           args=(configuration.getStateStoreConfig('usedclass'), configuration.getStateStoreConfig('availableclasses'), tmpinstance)))
                statestores[i].start()
            except config.ConfigException:
                print "Instance data is not valid. Skipping"
            finally:
                i += 1

    time.sleep(2)

    if configuration.startWriter():
        tmplen = configuration.getWriterInstanceNumber()
        print "Found writer configuration. Need to fork {} instances".format(tmplen)
        
        i = 0
        writers = []
        while i < tmplen:
            try:
                tmpinstance = configuration.getWriterConfigInstance(i)
                writers.append(Process(target=writerProcess,
                                       args=(configuration.getWriterConfig('usedclass'), configuration.getWriterConfig('availableclasses'), tmpinstance)))
                writers[i].start()
            except config.ConfigException:
                print "Instance data is not valid. Skipping"
            finally:
                i += 1
